"use strict"
import React from "react";

import objectPath from 'object-path';

import { Modal, Button, Icon } from "tinper-bee";

import Tree from "bee-tree";
const TreeNode = Tree.TreeNode;
import 'bee-tree/build/Tree.css';

import FormControl from "bee-form-control";
import 'bee-form-control/build/FormControl.css';

import "./index.less";

import { vendorsDanger as danger } from 'utils/config.js';

/**
 * @description: 带有模态框的树参照，1：选中操作：父子节点不受控，但租户根节点可选全部节点；取消选中状态：只取消点击节点。（多选）
 *                                  2：选中操作：父子节点受控。此功能是base组件的基础功能（多选）
 */
class CreateModalTree extends React.Component {
    constructor(props) {
        super(props);

        //初始化非真实缓存数据。
        let cacheData = this.createCacheData();

        this.state = {
            showModal: false,
            serachValue: '', //搜索框的值
            selectedKeysId: [],
            selectedKeysName: [],
            selectedKeysItem: [],
            defaultCheckedKeysId: [],
            defaultCheckedName: [],
            defaultCheckedItem: [],
            treeItems: [], //当前树的所有节点平铺
            treePks: [], //当前树的所有节点的主键平铺
            treeNames: [], //当前树的所有节点的名称平铺
            treeData: [],//当前树的数据
            sname: "sname",
            scode: "scode",
            refPk: "pkSrc",
            children: "children",
            title: '参照列表',
            isShowSearchArea: true, //显示搜索框
            isMultiSelectedEnabled: true, //树多选
            defaultExpandedKeys: [],
            expandedKeys: [],
            isCheckStrictly: true, //父子节点之间是否受控
            isCheckable: true, //显示checkbox框
            itemtype: "tree",
           // items: this.props.items, //模板数据,官网建议不要在state中使用this.props
            cacheData,
            curDataSource: "",//当前操作的模板的dataSource
            curTplIndex: 0, //当前打开的是第几个模板对应的数据
        };

        this.output = {
            isShow: this.isShow,
            getSelectDataByDataSouce: this.getSelectDataByDataSouce,//根据datasource获取选中节点的信息{id：[], name:[], item:[]}
            setSelectDataByDataSouce: this.setSelectDataByDataSouce,//根据datasource设置选中节点的信息{id：[], name:[], item:[]//item有，我也没设因为一般返回值里不会存在该字段的信息}
            clearSelectDataByDataSouce: this.clearSelectDataByDataSouce,//根据datasource清空树选中数据,
            getAllSelectData: this.getAllSelectData,//获取整个模板里配置的参照的选中数据
        }
    }

    //创建--初始化cache的数据。用于存储多个树的时候，导致会操作一个影响另一个。
    createCacheData = ()=> this.props.items.map((item, index)=>{
        return {
            ...item,
            treeData: [],
            selectedKeys: [],
            defaultCheckedKeysId: [],
            defaultCheckedName: [],
            defaultCheckedItem: [],
            defaultExpandedKeys: [],
            expandedKeys: [],
            selectedKeysId: [],
            selectedKeysName: [],
            selectedKeysItem: [],
            treeData: [],
            treeItems: [],
            treePks: [],
            treeNames: [],
            title: item.modalTitle

        }
    })
       

    /**
     * @description: 根据datasource获取选中节点的信息{id：[], name:[], item:[]}
     * @param {type} string  模板里对应的dataeSource字段
     * @return: 选中的数据obj
     */
    getSelectDataByDataSouce = (curClickDataSource = '') => {
        let { cacheData } = this.state,
            { items } = this.props,
            findIndex = this.getIndexByDataSource(curClickDataSource),
            resData = {}

        if (findIndex !== -1) {
            resData = {
                id: [...cacheData[findIndex].selectedKeysId],
                name: [...cacheData[findIndex].selectedKeysName],
                item: [...cacheData[findIndex].selectedKeysItem],
            }
        }

        return resData
    }

    /**
     * @description: 根据传入的datasource获取到非真实缓存里对应的位置。
     * @param {type} string  模板里对应的dataeSource字段
     * @return: cachedata数组里的位置
     */
    getIndexByDataSource = (curClickDataSource = '', data) => {
        let { cacheData } = this.state;

        if(!data){
            data = cacheData
        }

        let findIndex = data.findIndex((item, index, arr) => {

            return item.dataSource === curClickDataSource

        });

        return findIndex
    }

    /**
     * @description:  根据传入的datasource设置到非真实缓存里对应的数据，以及state里。
     * @param {curClickDataSource} string  模板里对应的dataeSource字段
     *        {id = [], name=[], item = []} object  想要设置选中节点的pks， names，和节点的集合[]
     */
    setSelectDataByDataSouce = (curClickDataSource = '', { id = [], name = [], item = [] } = {}) => {
        let { cacheData } = this.state,
            { items } = this.props,
            findIndex = this.getIndexByDataSource(curClickDataSource)

        if (findIndex !== -1) {

            cacheData[findIndex] = {
                ...cacheData[findIndex],
                selectedKeysId: [...id],
                selectedKeysName: [...name],
                selectedKeysItem: [...item],
                defaultCheckedKeysId: [...id],
                defaultCheckedItem: [...item],
                defaultCheckedName: [...name],
                defaultExpandedKeys: [...id],
                expandedKeys: [...id],
            }

            this.setState({
                cacheData,
                // selectedKeysId :  id,
                // selectedKeysName : name,
                defaultCheckedKeysId: id,
                defaultCheckedName: name,
                defaultCheckedItem: item,
                defaultExpandedKeys: id,
                //  expandedKeys: id,
            })
        }

    }

    /**
     * @description: 根据传入的datasource清空到非真实缓存里对应的数据，以及state里。
     * @param  {curClickDataSource} string  模板里对应的dataeSource字段
     */
    clearSelectDataByDataSouce = (curClickDataSource = '') => {
        let { cacheData } = this.state,
            { items } = this.props,
            findIndex = this.getIndexByDataSource(curClickDataSource);

        if (findIndex !== -1) {

            cacheData[findIndex] = {
                ...cacheData[findIndex],
                selectedKeysId: [],
                selectedKeysName: [],
                selectedKeysItem: [],
                defaultCheckedKeysId: [],
                defaultCheckedItem: [],
                defaultCheckedName: [],
                defaultExpandedKeys: [],
                expandedKeys: [],
            }
            this.setState({
                cacheData,
                // selectedKeysId :  id,
                // selectedKeysName : name,
                defaultCheckedKeysId: [],
                defaultCheckedName: [],
                defaultCheckedItem: [],
                selectedKeysId: [],
                selectedKeysName: [],
                selectedKeysItem: [],
                defaultExpandedKeys: [],
                expandedKeys: [],
            })
        }
    }

    /**
     * @description: 获取整个模板里配置的参照的选中数据
     * @return: 整个模板里配置的参照的选中数据
     */
    getAllSelectData = () => {
        let {
            cacheData
        } = this.state,
            res = {}

        cacheData.map((item) => {
            objectPath.set(
                res,
                [item.dataSource],
                {
                    id: item.selectedKeysId,
                    name: item.selectedKeysName,
                    item: item.selectedKeysItem,
                }
            )
        })

        return res;
    }

    /**
     * @description:  根据传入的datasource打开对应的模态框的结构, 找不到对应的就不打开模态框
     * @param  {curClickDataSource} string  模板里对应的dataeSource字段
     */

    //组合函数， 把函数合成函数组， 然后一起运行到结束
    componse = (...fns) =>{
        if(fns.length === 0){
            return (arg) => arg
        }
        if(fns.length === 1){
            return fns[0]
        }
        return (...args) =>fns.reduce((fns0, fns1) => fns0(fns1(...args)))
    }

    //打开模态框的时候，是哪个具体的树配置
    isShow = (curClickDataSource)=>{
        this.componse(this.initDataReq, this.setModalOpen)(curClickDataSource)
    }
    
    //设置模态框的打开， 并找到打开的模态框对应的数据，模板等
    setModalOpen = (curClickDataSource) => {
        let { cacheData } = this.state,
        { items } = this.props,
        findIndex = this.getIndexByDataSource(curClickDataSource, items);

        if (findIndex === -1) {
            return;
        }

        let reqRef = items[findIndex].reqRef,
            title = items[findIndex].modalTitle;

        

        //请求对应数据
        let reqData = reqRef.queryCondition && reqRef.queryCondition.call(this);
        //赋初始值
        let defaultObjs = this.updateCacheData("cancel", findIndex);
        this.setState({

            showModal: true,
            title,
            curTplIndex: findIndex,
            ...defaultObjs,

        })
        return {reqRef, reqData, findIndex}
       
    }

    //请求树的数据
    initDataReq = ({reqRef = {}, reqData = {}, findIndex = -1} = {}) => {
        if(findIndex === -1 || !Object.keys(reqRef).length) return

        let {
            cacheData,
        } = this.state;
        //默认post
        reqRef.method = reqRef.method ? reqRef.method : 'post'
        let dataKey = reqRef.method === "get" ? "params" : 'data';

        axios({
            method: reqRef.method,
            url: reqRef.refUrl,
            [dataKey]: reqData,
            headers: {
                'Cache-Control': 'no-cache',
            }

        }).then((res) => {

            if (res.data.resultCode !== 'busi_ex') {
                //遍历树， 平铺
                let { treeItems, treePks, treeNames } = this.mapTree([res.data.result], { treeItems: [], treePks: [], treeNames: [] })

                //存到对应cachedata
                cacheData[findIndex].treeData = [res.data.result];
                cacheData[findIndex].treeItems = treeItems;
                cacheData[findIndex].treePks = treePks;
                cacheData[findIndex].treeNames = treeNames;

                this.setState({
                    treeData: [res.data.result],
                    cacheData,
                    treeItems,
                    treePks,
                    treeNames
                })
            } else {
                danger({ 'text': res.data.exMsg })
            }
        })
    }

    /**
     * @description: 点击模态框的确定按钮， 同时触发模板里配置的方法， 如果需要的话
     * @param {type} :最终选中的数据
     */
    handleSelectedKeys = (endDatas) => {
        let {
            curDataSource,
        } = this.state,

            {
                selectedKeysId,
                selectedKeysName,
                selectedKeysItem,
            } = endDatas,
            { confirm } = this.props;

        confirm && confirm({
            id: [...selectedKeysId],
            name: [...selectedKeysName],
            item: [...selectedKeysItem],

        }, curDataSource)

    }

    //更新cachedata
    /**
     * @description: 更新cachedata数据
     * @param {type} string confirm === 类似于保存数据||cancel === 类似于还原数据, 
     * @return: {...cacheData更新后的cachedata, ...this.state的一些数据}。
     */
    updateCacheData = (operation = "confirm", curIndex) => {
        let {

            selectedKeysId,
            selectedKeysName,
            selectedKeysItem,
            cacheData,
            defaultCheckedKeysId,
            defaultCheckedName,
            defaultCheckedItem,
            curTplIndex
        } = this.state,
            updateObjs = {},
            curCacheData = cacheData[typeof curIndex !== "undefined" ? curIndex : curTplIndex],
            endResPks = [...selectedKeysId],
            endResItems = [...selectedKeysItem],
            endResNames = [...selectedKeysName],
            endResExpandpks = [...selectedKeysId]

        if (operation === "confirm") {
            let rootIndexN = selectedKeysId.indexOf("root")

            if (rootIndexN !== -1) {
                endResItems.splice(rootIndexN, 1)
                endResNames.splice(rootIndexN, 1)
                endResPks.splice(rootIndexN, 1)
                endResExpandpks.splice(rootIndexN, 1)
            }

            updateObjs = {
                ...curCacheData,
                defaultCheckedKeysId: [...selectedKeysId],
                defaultCheckedName: [...selectedKeysName],
                defaultCheckedItem: [...selectedKeysItem],
                selectedKeysId: [...endResPks],
                selectedKeysName: [...endResNames],
                selectedKeysItem: [...endResItems],
                defaultExpandedKeys: [...endResExpandpks],
                expandedKeys: [...endResExpandpks],
            }

        } else if (operation === "cancel") {

            updateObjs = {
                ...curCacheData,
                defaultCheckedKeysId: [...curCacheData.defaultCheckedKeysId],
                defaultCheckedName: [...curCacheData.defaultCheckedName],
                defaultCheckedItem: [...curCacheData.defaultCheckedItem],
                defaultExpandedKeys: [...curCacheData.defaultExpandedKeys],
                expandedKeys: [...curCacheData.defaultExpandedKeys],
                selectedKeysId: [...curCacheData.defaultCheckedKeysId],
                selectedKeysName: [...curCacheData.defaultCheckedName],
                selectedKeysItem: [...curCacheData.defaultCheckedItem],
            }

        }
        cacheData[typeof curIndex !== "undefined" ? curIndex : curTplIndex] = {
            ...cacheData[typeof curIndex !== "undefined" ? curIndex : curTplIndex],
            ...updateObjs
        }

        return {
            cacheData,
            ...updateObjs
        }
    }

    /**
     * @description: 关闭模态框：取消或者确定
     * @param {type} ：点击的是关闭的按钮，还是确定的按钮 string ||undefined  cancel和undefined是代表关闭模态框；confirm是点击确定按钮
     */
    close = (source) => {

        this.setState({

            showModal: false

        }, () => {
            let updateCacheDatas = {};
            if (source === "confirm") {

                updateCacheDatas = this.updateCacheData("confirm")
                this.handleSelectedKeys(updateCacheDatas);

            } else {

                updateCacheDatas = this.updateCacheData("cancel")

            }

            this.setState({
                ...updateCacheDatas,
                serachValue: ''
            })
        })
    }
    
    //TODO：父子受控时， 节点之前与checkbox的关系， 以及半个选中态的关系，还没写。
    onSelect = (selectedKeys, nodeParam) => {
        let {

            isCheckStrictly,
            isMultiSelectedEnabled

        } = this.state;

        if(!isCheckStrictly){
            return
        }

        nodeParam["checkedNodes"] = nodeParam.selectedNodes
        nodeParam["checked"] = nodeParam.selected 
        
        this.onCheck(selectedKeys, nodeParam)
    }

    /**
     * @description: 树节点的选中，checkbox的操作
     * @param {selectedKeys} //tree组件自带的函数的参数，选中的节点的pks集合
     *        {nodeParam} //tree组件自带的函数的参数，选中的节点的信息
     * @return: 
     */
    onCheck = (selectedKeys, nodeParam) => {
        let _me = this,
            {
                treeItems,
                treeNames,
                treePks,
                selectedKeysId,
                selectedKeysItem,
                selectedKeysName,
                expandedKeys,
                isCheckStrictly
            } = _me.state;

        if (_me.state.isMultiSelectedEnabled) {//多选
            let [
                    {
                        resPks ,
                        resItems ,
                        resNames
                    }
            ]  = this.mapCheckNodes(nodeParam.checkedNodes, { resPks : [], resItems : [], resNames : [] });//遍历是为了，在父子联动时， 保证三个可以， name，item字段的值统一

            selectedKeysItem = resItems;
            selectedKeysName = resNames;
            selectedKeysId = resPks;

            if (nodeParam.node.props.eventKey !== "root") {//取消选中的是非根节点---非租户，则自动把根节点取消选中。
                let rootIndexN = selectedKeysId.indexOf("root")
                if (rootIndexN !== -1) {
                    selectedKeysItem.splice(rootIndexN, 1)
                    selectedKeysName.splice(rootIndexN, 1)
                    let rootIndex = selectedKeys.indexOf("root");
                    selectedKeys.splice(rootIndex, 1)
                }

            }

        } else {//单选
            if (nodeParam.checked) {

                selectedKeysItem = [nodeParam.node.props.item]
                selectedKeysName = [nodeParam.node.props.name]
                selectedKeysId = [nodeParam.node.props.eventKey]
            } else {

                selectedKeysItem = []
                selectedKeysName = []
                selectedKeysId = []

            }
        }

        //取消选中
        if (!nodeParam.checked) {
            expandedKeys = [...expandedKeys]
        } else {
            expandedKeys = [...selectedKeysId]
        }
        _me.setState({

            selectedKeysId: [...selectedKeysId],
            selectedKeysName: [...selectedKeysName],
            selectedKeysItem: [...selectedKeysItem],
            expandedKeys: [...expandedKeys],

        })
    }

    /**
     * @description: 操作节点或checkbox时， 选中的节点的遍历
     * @param {type} 
     * @return: [{ resPks = [], resItems = [], resNames = [] }]
     */
    mapCheckNodes = ( data, { resPks = [], resItems = [], resNames = [] } = {}) => data.map((item, index) => {
        resPks.push(item.key);
        resItems.push(item.props.item);
        resNames.push(item.props.name);

        if (item.children) {
            this.mapCheckNodes(item.children, { resPks, resItems, resNames })
        }
        return {
            resPks, resItems, resNames
        }
    })


    /**
     * @description: 树节点的展开
     * @param {type} 
     * @return: 
     */
    onExpand = (expandedKeys, { expanded, node }) => {
        if (!expanded) {
            if (node.props.eventKey === "root") {
                expandedKeys = []
            }
        }
        this.setState({
            expandedKeys
        })
    }

    /**
     * @description: 生成树
     * @param {type} 
     * @return: 
     */
    loop = data => data.map((item, index) => {

        let titleSearch = '',
            {
                isShowSearchArea,
                selectedKeysId,
                sname,
                scode,
                refPk,
                children,
                serachValue,
                isMultiSelectedEnabled
            } = this.state;

        if (item[children]) {
            return (
                <TreeNode
                    key={item[refPk] ? item[refPk] : "root"}//根节点默认为租户。
                    title={item[sname]}
                    item={item}
                    name={item[sname]}
                    icon={this.renderTreeNodeIcon(item.children, item)}
                    titleStyle={{ color: serachValue && item[sname].includes(serachValue) ? "#ff5f5f" : '#000' }}
                    disableCheckbox={!isMultiSelectedEnabled && !item[refPk]}//单选并且不是根节点
                >
                    {item[children] ? this.loop(item[children]) : null}
                </TreeNode>
            )
        }
        return <TreeNode
            icon={this.renderTreeNodeIcon(item.children, item)}
            key={item[refPk] ? item[refPk] : item["root"]}
            title={item[sname]}
            name={item[sname]}
            item={item}
            disableCheckbox={!isMultiSelectedEnabled && !item[refPk]}//单选并且不是根节点
            titleStyle={{ color: serachValue && item[sname].includes(serachValue) ? "#ff5f5f" : '#000' }}
        />;

    });

    /**
     * @description: 查询区
     * @param {type} 
     * @return: 
     */
    getSearchDataInTree() {
        let {
            serachValue,
            treeNames,
            treePks
        } = this.state,
            findIndex = [],
            depandKeys = []

        if (!serachValue) {
            return;
        }


        //找到搜索框里所在节点的数据
        treeNames.map((item, index) => {
            if (item.includes(serachValue)) {
                depandKeys.push(treePks[index])
                findIndex.push(index)
            }
        })

        //找到了就自动出现在视窗内
        this.setState({
            expandedKeys: depandKeys
        }, () => {
            findIndex.length && this.scrollToAnchor(findIndex[0]);
        })
    }

    //    /*搜索的树定位*/
    scrollToAnchor(index) {
        //设置0.2s是因为，渲染后，不能自动立马滑到对应位置。
        setTimeout(() => {
            document.getElementsByClassName("u-tree-title")[index] && document.getElementsByClassName("u-tree-title")[index].scrollIntoView();
        }, 200)
    }

    /**
     * @description: 遍历树
     * @param {type} 
     * @return: 
     */
    mapTree = (treeData, res = { treeItems: [], treePks: [], treeNames: [] }) => {
        let {
            children,
            sname,
            refPk
        } = this.state;

        treeData.map((item, index) => {

            res["treeItems"].push(item);
            res["treePks"].push(item[refPk] || "root");
            res["treeNames"].push(item[sname]);

            if (item[children] && item[children].length) {
                this.mapTree(item[children], res)
            }
        })

        return res;
    }

    //渲染节点前的icon，图标，父节点和子节点不一样的图标。
    renderTreeNodeIcon = (childData) => {
        let iconType = ""
        if (childData && Array.isArray(childData) && childData.length > 0) {
            iconType = "uf-treefolder"
            return <Icon type={iconType} />
        } else {
            iconType = 'uf-list-s-o'
        }
        return <Icon type={iconType} />
    }

    //模态框body位置显示的内容
    modalContent = () => {
        let {
            treeData,
            selectedKeysId,
            isCheckStrictly,
            isMultiSelectedEnabled,
            expandedKeys,
            defaultExpandedKeys,
            defaultCheckedKeysId,
            halfSelectedKeysId 
        } = this.state;


        let node = null;
        node = <Tree
            showIcon
            showLine={true}
            closeIcon={<Icon type="uf-add-s-o" />}
            openIcon={<Icon type="uf-reduce-s-o" />}
            defaultExpandedKeys={defaultExpandedKeys}
            expandedKeys={expandedKeys}
            onExpand={this.onExpand}
            selectedKeys={isCheckStrictly ?selectedKeysId :[]}
            checkedKeys={selectedKeysId}
            defaultCheckedKeys={defaultCheckedKeysId}
            className={'modal-tree'}
            onSelect={this.onSelect}
            onCheck={this.onCheck}
            treeData={treeData}
            multiple={isMultiSelectedEnabled && isCheckStrictly}//是节点的多个选中，不是checkbox框
            checkable={true}
            checkStrictly={isMultiSelectedEnabled ? isCheckStrictly ? isCheckStrictly : false : true}
        >
            {this.loop(treeData)}
        </Tree>
        if(!node.props.children.length){
            node = <div className = "tree-none-data">暂无数据</div>
        }
        return (node)
    }

    render() {
        let {
            showModal,
            title,
            isShowSearchArea,
            selectedKeysId,
            serachValue,
        } = this.state;

        return (
            <div>
                <Modal
                    backdrop={false}
                    show={showModal}
                    onHide={this.close}
                    size={"lg"}
                >
                    <Modal.Header closeButton >
                        <Modal.Title>{title}</Modal.Title>
                    </Modal.Header>
                    <Modal.Body>

                        {
                            isShowSearchArea ?
                                <div className={'search-header'}>
                                    <div className={'search-header-search'}>
                                        <FormControl
                                            value={serachValue}
                                            onChange={(value) => {

                                                //找到了就自动出现在视窗内
                                                this.setState({
                                                    serachValue: value,

                                                }, () => {
                                                    this.getSearchDataInTree()

                                                })
                                            }}
                                            placeholder="请输入要查询的内容"
                                            emptyText={() => '暂无数据'}
                                            className={'searchFormControl'}
                                            showClose={true}
                                        />
                                    </div>
                                </div> :
                                null
                        }
                        {this.modalContent()}
                    </Modal.Body>
                    <Modal.Footer className={"footerButton"}>
                        <Button onClick={() => this.close()} className={"cancel"} >取消</Button>
                        <Button
                            onClick={() => this.close("confirm")}
                            className={"confirm"}
                            disabled={selectedKeysId.length ? false : true}
                            bordered
                        >确认</Button>
                    </Modal.Footer>
                </Modal>
            </div>
        )
    }
}
function YGCreateModalTree(treemodalTpl) {
    return (
        <CreateModalTree
            ref={(refs) => { this.modalTreeC = {"output" : refs && refs['output']}; console.log(refs) }}
            {...treemodalTpl}
            {...treemodalTpl.events}
        />
    )
}

export default YGCreateModalTree;